/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package com.guchenbo.example.netty;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.security.auth.callback.*;
import javax.security.sasl.RealmCallback;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * Handles a client-side channel.
 */
@Sharable
public class SaslClientHandler extends SimpleChannelInboundHandler<SaslMessage> {
    protected static final Logger log = LoggerFactory.getLogger(SaslServerHandler.class);

    private SaslClient saslClient;

    public SaslClientHandler() {
        String username = "myUser";
        String password = "myPassword";

        String mechanism = "DIGEST-MD5"; // 选择SASL机制
        String protocol = "example"; // 使用的协议
        String serverName = "myServer"; // 服务器名
        // 创建客户端回调处理程序
        CallbackHandler callbackHandler = new CallbackHandler() {
            @Override
            public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
                for (Callback callback : callbacks) {
                    if (callback instanceof NameCallback) {
                        NameCallback nameCallback = (NameCallback) callback;
                        nameCallback.setName(username);
                    } else if (callback instanceof PasswordCallback) {
                        PasswordCallback passwordCallback = (PasswordCallback) callback;
                        passwordCallback.setPassword(password.toCharArray());
                    } else if (callback instanceof RealmCallback) {
                        RealmCallback rc = (RealmCallback) callback;
                        //Collect realm data in application-specific manner
                        rc.setText("myServer");
                    } else {
                        log.warn("UnsupportedCallbackException {}", callback);
                        throw new UnsupportedCallbackException(callback, "Unsupported Callback");
                    }
                }
            }
        };
        Map<String, String> props = new HashMap<>();
        props.put(Sasl.QOP, "auth"); // SASL质量保证参数

        try {
            this.saslClient = Sasl.createSaslClient(new String[] { mechanism }, null, protocol, serverName, props,
                            callbackHandler);
        } catch (SaslException e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, SaslMessage request) throws Exception {
        log.info("request {}", request.getMsg());
        byte[] challenge = saslClient.evaluateChallenge(request.getChallenge());
        if (challenge != null) {
            SaslMessage response = new SaslMessage();

            response.setMsg("server");
            response.setChallenge(challenge);
            ChannelFuture future = ctx.writeAndFlush(response);
            future.sync();
        }
        System.out.println(saslClient.isComplete());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}
